<?php
###
#  Baing PHP 框架 1.0.0
#  http://baing.co/
#
#  Copyright: 白晓阳
#  License: CC BY-NC-SA 4.0
#  https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh
###

namespace bai;

use bai\resource\Document;


final class Response extends Baing
{
    public const STATUS_OK = 200;
    public const STATUS_CREATED = 201;
    public const STATUS_UPDATED = 202;
    public const STATUS_DELETED = 204;
    public const STATUS_REDIRECT = 302;
    public const STATUS_REDIRECT_X = 203;
    public const STATUS_BAD_REQUEST = 400;
    public const STATUS_UNAUTHORIZED = 401;
    public const STATUS_FORBIDDEN = 403;
    public const STATUS_NOT_FOUND = 404;
    public const STATUS_INVALID = 422;
    public const STATUS_ERROR = 500;
    public const STATUSES = [
        self::STATUS_OK, self::STATUS_CREATED, self::STATUS_UPDATED, self::STATUS_DELETED,
        self::STATUS_REDIRECT, self::STATUS_REDIRECT_X, self::STATUS_BAD_REQUEST, self::STATUS_UNAUTHORIZED,
        self::STATUS_FORBIDDEN, self::STATUS_NOT_FOUND, self::STATUS_INVALID, self::STATUS_ERROR,
    ];

    protected static array $getters = ['status'];

    protected array $runtime = [
        'configurable' => false,
        'status' => self::STATUS_OK,
        'headers' => [],
        'cookies' => [],
        'responses' => [],
        'resources' => [],
    ];

    public static function access(): self
    {
        return self::$response;
    }

    public function run()
    {
        foreach ($this->runtime['resources'] as $resource) {
            $this->addResponse($resource->run());
        }
        foreach ($this->runtime['headers'] as $header) {
            call_user_func_array('header', $header);
        }
        foreach ($this->runtime['cookies'] as $cookie) {
            call_user_func_array('setcookie', $cookie);
        }
        foreach ($this->runtime['responses'] as $response) {
            print $response;
        }
        http_response_code($this->runtime['status'] ?: self::STATUS_OK);
    }

    public function setStatus(int $status)
    {
        if (in_array($status, self::STATUSES)) {
            $this->runtime['status'] = $status;
        }
    }

    public function addHeader(string $name, string $value)
    {
        if (!$name) return;
        $key = strtolower($name);
        $this->runtime['headers'][$key] = ["$name: $value"];
        if ($key === 'location') {
            $this->setStatus(self::$request->ajax ? self::STATUS_REDIRECT_X : self::STATUS_REDIRECT);
        }
    }

    public function addCookie(string $name, string $value, int $period = 0, bool $httponly = true)
    {
        if (!$name) return;
        $options = ['httponly' => $httponly, 'path' => '/'];
        if ($period != 0) {
            $options['expires'] = time() + $period;
        }
        $cookie[] = [$name, $value, $options];
        $this->runtime['cookies'][strtolower($name)] = $cookie;
    }

    public function addResponse(string|array $content, bool $refresh = false)
    {
        if ($refresh) {
            $this->runtime['responses'] = [];
            if ($this->runtime['status'] >= self::STATUS_BAD_REQUEST) {
                $this->setStatus(self::STATUS_OK);
            }
        }
        if (is_string($content)) {
            $this->runtime['responses'][] = $content;
            return;
        }
        $this->runtime['responses'][] = json_encode($content);
    }

    public function addResource(Document $resource, bool $refresh = false)
    {
        if ($refresh) {
            $this->runtime['resources'] = [];
            if ($this->runtime['status'] >= self::STATUS_BAD_REQUEST) {
                $this->setStatus(self::STATUS_OK);
            }
        }
        $this->runtime['resources'][] = $resource;
    }

    public function __destruct()
    {
        $this->runtime['resources'] = [];
    }
}
